#pragma once
#include <functional>
#include "../interface/ClientExecutionInterface.hpp"
#include "../../config/client/ClientIOCPConfig.hpp"
#include "../../execution/client/ClientMainExecution.hpp"
#include "../../tool/NetworkIOTool.hpp"

/**
 * 总共需要连接的服务器
 * 1.雪花id服务器
 * 2.转发消息的服务器
 */
class ClientIOCPExecution : public ClientExecutionInterface{

private:

    /**
     *服务器节点
     */
    class Client : public NetworkIOTool::NodeInterface{

    private:
        static constexpr NetworkIOTool::NODE_TYPE nodeType = NetworkIOTool::NODE_TYPE::CLIENT;

        //预备服务器信息
        std::queue<NetworkIOTool::PrePareServerInformation> m_prePareServers;

    private:

        /**
         *
         * @param context
         * @param bytesTransferred
         */
        void jumpHandleFun(NetworkIOTool::IOContext *context, DWORD bytesTransferred) override {
            switch (context->m_operation) {
                case NetworkIOTool::IO_OPERATION::READ:
                    this->handleRead(context,bytesTransferred);
                    break;
                case NetworkIOTool::IO_OPERATION::WRITE:
                    this->handleWrite(context,bytesTransferred);
                    break;
                default:
                    break;
            }
        }

        /**
         *
         * @param prePareServer
         */
        void connectServer(NetworkIOTool::PrePareServerInformation& prePareServer) {

            //创建Socket
            SOCKET serverSocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
            if (serverSocket == INVALID_SOCKET) {
                this->handleError("WSASocket",true);
                return;
            }

            //解析服务器地址
            sockaddr_in serverAddr;
            serverAddr.sin_family = AF_INET;
            serverAddr.sin_port = htons(prePareServer.m_port);
            inet_pton(AF_INET,prePareServer.m_IP,&serverAddr.sin_addr);

            //连接服务器
            if (connect(serverSocket,(sockaddr*)&serverAddr,sizeof(serverAddr)) == SOCKET_ERROR) {

                this->handleError("connect");
                closesocket(serverSocket);
                return;
            }

            //将socket关联到IOCP
            if (CreateIoCompletionPort(
                (HANDLE)serverSocket,
                this->m_iocpHandle,
                (ULONG_PTR)serverSocket,
                0) == NULL) {

                this->handleError("CreateIoCompletionPort");
                closesocket(serverSocket);
                CloseHandle(this->m_iocpHandle);
                return;
            }

            this->m_running = true;

            //加入远程节点
            std::string IPPortStr = std::string(prePareServer.m_IP)+":"+std::to_string(prePareServer.m_port);
            std::shared_ptr<NetworkIOTool::NodeInformation> nodeContextData(new NetworkIOTool::NodeInformation(prePareServer.m_serverName,IPPortStr,serverSocket));
            this->m_remoteNodesData.addRemoteNode(nodeContextData);

            //注册上下文
            NetworkIOTool::IOContext* context = this->m_IOContextPool.acquireIOContext();
            context->m_socket = serverSocket;


            //投递读操作
            this->postRead(context);

            //调用连接回调函数
            if (this->m_onConnected != NULL) this->m_onConnected(serverSocket);
        }

        /**
        * 启动回到调函数
        */
        void startCallBackFun() override {
            //初始化WinSocket
            WSAData wsaData;
            if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
                this->handleError("WSAStartup",true);
                return;
            }

            //创建IOCP
            this->m_iocpHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
            if (this->m_iocpHandle == NULL) {
                this->handleError("CreateIoCompletionPort",true);
                return;
            }

            //预备服务器为空
            if (this->m_prePareServers.empty())
                throw std::runtime_error("prePareServers is empty");

            //连接服务器
            while (!this->m_prePareServers.empty()) {
                this->connectServer(this->m_prePareServers.front());
                this->m_prePareServers.pop();
            }

            // 仅当有有效连接时才创建工作线程
            if (this->m_running)
                for (size_t i = 0; i < this->m_maxConcurrentThreads ; i++)
                    this->m_workerThreads.emplace_back(&Client::workerThread,this);
        }


        /**
         * 关闭回调函数
         */
        void shutDownCallBackFun() override {}


    public:
        Client() {
            this->m_running = false;

        }

        ~Client() override {

        }


        /**
         *
         * @param IP
         * @param port
         */
        void addServer(std::string& nodeName,const char* IP, int port) {
            if (this->m_running) return;
            this->m_prePareServers.push(std::move(NetworkIOTool::PrePareServerInformation(nodeName,IP,port)));
        }

        /**
         *
         * @param prePareServer
         */
        void addServer(NetworkIOTool::PrePareServerInformation& prePareServer) {
            if (this->m_running) return;
            this->m_prePareServers.push(prePareServer);
        }

        /**
         *
         * @return
         */
        NetworkIOTool::NODE_TYPE getNodeType() override {
            return Client::nodeType;
        }
    };


    /**
     * 服务器类型
     */
    enum SERVER_TYPE {
        ID_SERVER,
        FORWARD_SERVER
    };


    /**
     * 消息队列
     */
    using  MessageStr = std::string;
    class MessageStrsQueue {
    private:

        std::mutex m_mutex;
        std::queue<MessageStr> m_messageStrs;
        std::condition_variable m_condition;

    public:

        MessageStrsQueue() {}
        ~MessageStrsQueue() {}

        /**
         *
         * @return
         */
        MessageStr getMessageStr() {
            std::unique_lock<std::mutex> lock(this->m_mutex);
            this->m_condition.wait(lock, [this](){return this->m_messageStrs.empty();});
            MessageStr message = this->m_messageStrs.front();
            this->m_messageStrs.pop();
            return message;
        }

        /**
         *
         * @param message
         */
        void pushMessage(MessageStr& message) {
            std::unique_lock<std::mutex> lock(this->m_mutex);
            this->m_messageStrs.push(message);
            //唤醒处理线程
            this->m_condition.notify_one();
        }

        /**
         *
         * @return
         */
        std::mutex& getMutex() {
            return this->m_mutex;
        }

        /**
         *
         * @param handleMessageStrFun
         */
        void handleQueue(std::function<bool(MessageStr& message)> handleMessageStrFun) {
            std::unique_lock<std::mutex> lock(this->m_mutex);
            while (true) {
                this->m_condition.wait(lock, [this](){return this->m_messageStrs.empty();});
                MessageStr message = this->m_messageStrs.front();
                if (handleMessageStrFun(message))
                    this->m_messageStrs.pop();
            }
        }

        /**
         *
         */
        void clearQueue() {
            std::unique_lock<std::mutex> lock(this->m_mutex);
            std::queue<MessageStr>().swap(this->m_messageStrs);
        }
     };


private:


    using AddressStr = std::string;


    std::unordered_map<SERVER_TYPE,AddressStr> m_serverInfo;

    Client* m_client;

    static ClientType::EXECUTION_TYPE executionType;

    MessageStrsQueue m_sendIDServerMessageQueue;

    MessageStrsQueue m_sendForwardServerMessageQueue;

    MessageStrsQueue m_receiveMessageQueue;


    std::function<void(SOCKET,const char* ,int)>  m_receiveMessageCallFun;

    std::function<bool(MessageStr& message)> m_handleSendIDServerMessageStrFun;

    std::function<bool(MessageStr& message)> m_handleSendForwardServerMessageStrFun;

private:




    /**
     * 启动回调函数
     * @return
     */
    bool startCallBackFunction() override {

        NetworkIOTool::PrePareServerInformation idServer = ((ClientIOCPConfig*)this->m_config)->m_iDServerInfo;
        NetworkIOTool::PrePareServerInformation forwardServer = ((ClientIOCPConfig*)this->m_config)->m_forwardServerInfo;

        //预备服务器为空
        if (idServer.empty() || forwardServer.empty())
            throw std::runtime_error("idServer is empty || forwardServer is empty");

        //注册服务器信息
        this->m_serverInfo[SERVER_TYPE::ID_SERVER] = std::string(idServer.m_IP)+":"+std::to_string(idServer.m_port);
        this->m_serverInfo[SERVER_TYPE::FORWARD_SERVER] = std::string(forwardServer.m_IP)+":"+std::to_string(forwardServer.m_port);

        //添加服务器
        this->m_client->addServer(idServer);
        this->m_client->addServer(forwardServer);


        //配置接收信息回调函数
        this->m_client->setOnDataReceived(this->m_receiveMessageCallFun);

        //获取多线程启动执行器
        ClientThreadExecution* threadExecution =
           dynamic_cast<ClientThreadExecution*>(
               dynamic_cast<ClientMainExecution*>(
                   this->m_mainExecution)->getExecutionByExecutionType(ClientType::CLIENT_THREAD_EXECUTION));


        //多线程启动IOCP服务器
        threadExecution->enqueue([this]() {this->m_client->start();});

        //多线程启动转发服务器消息队列
        threadExecution->enqueue([this]{this->m_sendIDServerMessageQueue.handleQueue(this->m_handleSendIDServerMessageStrFun);});

        //多线启动ID服务器消息队列
        threadExecution->enqueue([this]{this->m_sendForwardServerMessageQueue.handleQueue(this->m_handleSendForwardServerMessageStrFun);});
    }

    /**
     *
     * @return
     */
    bool closeCallBackFunction() override {


        this->m_receiveMessageQueue.clearQueue();
        this->m_sendIDServerMessageQueue.clearQueue();
        this->m_sendForwardServerMessageQueue.clearQueue();

    }

public:

    /**
     *
     * @param messageStr
     */
    void addSendIDServerMessageStr(MessageStr& messageStr) {
        this->m_sendIDServerMessageQueue.pushMessage(messageStr);
    }


    /**
     *
     * @param messageStr
     */
    void addSendForwardServerMessageStr(MessageStr& messageStr) {
        this->m_sendForwardServerMessageQueue.pushMessage(messageStr);
    }

    /**
     *
     * @return
     */
    MessageStr getReceviceMessage() {
        return this->m_receiveMessageQueue.getMessageStr();
    }

    /**
     *
     * @return
     */
    ClientType::EXECUTION_TYPE getType() const override {
        return ClientIOCPExecution::executionType;
    }

    /**
     *
     */
    ClientIOCPExecution() {

        this->m_client = nullptr;

        this->m_receiveMessageCallFun= [this](SOCKET socket,const char* message ,int len) {
            std::string msg(message);
            this->m_receiveMessageQueue.pushMessage(msg);
        };

        this->m_handleSendIDServerMessageStrFun = [this](MessageStr& message)->bool {
            this->m_client->send(this->m_serverInfo[SERVER_TYPE::ID_SERVER],message.c_str());
        };

        this->m_handleSendForwardServerMessageStrFun = [this](MessageStr& message)->bool {
            this->m_client->send(this->m_serverInfo[SERVER_TYPE::FORWARD_SERVER],message.c_str());
        };
    }


    /**
     *
     */
    ~ClientIOCPExecution() override {
        if (this->m_client != nullptr) {
            delete this->m_client;
            this->m_client = nullptr;
        }


    }
};

ClientType::EXECUTION_TYPE ClientIOCPExecution::executionType = ClientType::EXECUTION_TYPE::CLIENT_IOCP_EXECUTION;

